import { App } from "../../../../src/App";
import { PokerData, PokerSuit } from "../../../src/BaseLogic";
import GameLogic from "../Game/GameLogic";
import { GameModule } from "../Game/GameModule";

const { menu, ccclass, property } = cc._decorator;


@menu("自定义插件/扑克墙组件")

class PokerItem extends cc.Component {
    private _front: cc.Node = null;
    private _back: cc.Node = null;
    private _flag: cc.Node = null;
    private _data:PokerData = null;
    private _isFront:boolean = false;
    private _isMask:boolean = false;
    private _selHeight:number = 0;

    /**
     *设置扑克值
     *
     * @memberof Poker
     */
    public set data(data:PokerData){
        this._front = App.Util.find<cc.Node>('imgFront',this);
        this._back = App.Util.find<cc.Node>('imgBack',this);
        this._flag = App.Util.find<cc.Node>('imgFlag',this);
        const multi = this._back.getComponent('MultiPlug');
        if (App.Util.isValid(multi)){
            const cfg = App.getLocalStorage(App.LOCAL_KEY.SET);
            const style = parseInt(App.Util.trim(cfg ? cfg['style']:'-1'));
            style >= 0 && multi.setValue(style);
        }

        this._front.active = false;
        this._flag.active = false;
        this._back.active = true;
        this.mask =false;
        this._data = data;
        this.node.name = `Poker_${data.num}_${data.name}`;
        (this._front.getComponent(cc.Sprite) || this._front.addComponent(cc.Sprite)).spriteFrame = GameModule.getPokerSprite(data.num);
    }

    /**
     * 获取扑克数据
     *
     * @readonly
     * @type {PokerData}
     * @memberof Poker
     */
    public get data():PokerData{return this._data}

    /**
     * 设置是否选中
     */
    public set select(is:boolean){ this.node.y = (is ? this._selHeight : 0); }

    /**
     * 获取当前节点是否被选中
     */
    public get isSelect():boolean{ return this.node.y !== 0; }

    public get isMask():boolean {return this._isMask;}

    /**
     *设置遮罩
     *
     * @memberof Poker
     */
    public set mask(is:boolean){
        this._front.color = is ? cc.color(130,130,130) : cc.color(255,255,255);
        this._isMask = is;
    }

    public set selectHight(v:number){this._selHeight = v}

    /**
     * 是否显示标签
     */
    public set showFlag(is:boolean){this._flag.active = is;}

    /**
     * 翻牌动画
     * @param isFront 是否展示牌面
     * @param cb 
     * @returns 
     */
    public set showFront(isFront:boolean){
        cc.tween(this.node).stop;
        if (this._isFront !== isFront){
            cc.tween(this.node)
            .to(0.15,{scaleX:0,scaleY:1},{easing:'quadIn'})
            .call(()=>{
                this._back.active = !isFront;
                this._front.active = isFront;
                this.node.scaleX = 1;
                this._isFront = isFront;
            }).start();
        }
    }
}

@ccclass
export default class PokerWall extends cc.Component {
    @property({ type: cc.Prefab, tooltip: '扑克牌墙' })
    pokerPrefab: cc.Prefab = null;
    @property({ tooltip: '选择提起高度' })
    selectHight: number = 0;

    private _spacing:cc.Vec2 = cc.v2(0,0); // 子对向排列间隔
    private _itemSize:cc.Size = cc.size(0,0); // 子对象尺寸
    private _touchEvent: cc.Touch = null;
    private _isTouch:boolean = false;
    private _isMove:boolean = false; // 是否滑动
    private _thouchAreaWidth:number = 0; // 触碰区宽度
    private _datas:PokerData[] = [];

    start() {
        // 防止扑克牌根节点尺寸小于图片尺寸造成扑克墙尺寸计算失败
        const tmp:cc.Node = cc.instantiate(this.pokerPrefab);
        this._itemSize = tmp.getChildByName('imgFront').getContentSize();
        tmp.setContentSize(this._itemSize);
        this.node.setContentSize(this._itemSize);
        this.node.removeAllChildren(); // 移除扑克模板
        const layout = this.getComponent(cc.Layout);
        this._spacing = cc.v2(layout.spacingX,layout.spacingY);
        this._thouchAreaWidth = Math.abs(this._itemSize.width - Math.abs(this._spacing.x))*this.node.scale;

        this.node.on(cc.Node.EventType.TOUCH_START, this._onTouchStart, this);
        this.node.on(cc.Node.EventType.TOUCH_END, this._onTouchEnd, this);
        this.node.on(cc.Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
        this.node.on(cc.Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
    }

    onDestroy(){
        this.node.off(cc.Node.EventType.TOUCH_START, this._onTouchStart, this);
        this.node.off(cc.Node.EventType.TOUCH_END, this._onTouchEnd, this);
        this.node.off(cc.Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
        this.node.off(cc.Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
    }

    public set touch(is:boolean){this._isTouch = is}

    /**
     * 设置子节点排列间隔
     * @param spacing 
     * @param y 
     */
    public setSpacing(spacing: cc.Vec2|number, y?: number){
        const layout = this.getComponent(cc.Layout);
        if (typeof(spacing) === 'number') layout.spacingX = spacing;
        else if (typeof(y) === 'number')layout.spacingY = y;
        else if (spacing instanceof cc.Vec2){
            layout.spacingX = spacing.x;
            layout.spacingY = spacing.y;
        }
    }

    public get itemSize():cc.Size {return this._itemSize;}

    /**
     * 获取扑克墙上所有扑克
     *
     * @readonly
     * @type {PokerData[]}
     * @memberof PokerWall
     */
    public get datas():PokerData[]{return this._datas;}

    public showFlag(is:boolean=false){
        this.node.children.forEach((it:cc.Node)=>{
            it.getComponent(PokerItem).showFlag = is;
        });
    }

    /**
     * 获取已选择的扑克数据
     * 
     * @readonly
     * @type {PokerData[]}
     * @memberof PokerWall
     */
    public get selects():PokerData[]{
        let sel:PokerData[] = [];
        for (let it of this.node.children){
            const item:PokerItem = it.getComponent(PokerItem);
            item.isSelect && sel.push(item.data);
        } 
        return sel;
    }

    public autoSelect(cards:PokerData[]){
        while(cards.length > 0){
            const card = cards.shift();
            for (let it of this.node.children){
                const item:PokerItem = it.getComponent(PokerItem);
                if (card && card.num === item.data.num){
                    item.select = true;
                    break ;
                }
            }
        }
    }

    public cancelSelect(){
        this.node.children.forEach((it)=>{
            it.getComponent(PokerItem).select = false;
        });
    }

    /**
     * 从扑克墙上移除指定扑克牌对象，若为空或者null则移除所有
     * @param list 待移除队列
     */
    public remove(list?:PokerData[]){
        const children:cc.Node[] = this.node.children;
        if (children.length < 1) return ;
        let tmpList = list ? list.slice(0) : [];
        if (tmpList.length > 0){
            while (tmpList.length > 0){
                const rem = tmpList.shift();
                const idx = this._datas.indexOf(rem);
                if (idx > -1){
                    for (let i = children.length-1; i >= 0; --i){
                        const item = children[i].getComponent(PokerItem);
                        if (item && item.data === rem){
                            this._datas.splice(idx,1);
                            GameModule.removePoker(item);
                            item.destroy();
                            break ;
                        }
                    }
                }
            }
        } else {
            for (let i = children.length-1; i >= 0; --i){
                children[i].removeComponent(PokerItem);
                GameModule.removePoker(children[i]);
            }
            this._datas = [];
        }
    }

    /**
     * 向扑克墙里插入扑克牌
     * @param list 扑克逻辑值队列
     * @param isAutoSort 是否排序动画，即自己的时候打开，其他关闭进入默认排序，这里涉及扑克对象节点深度排序
     * @param isFront 是否显示牌面
     * @param isFlag 是否显示扑克标记，比如在首牌显示地主
     * @returns 
     */
    public insert(list:PokerData[],isAutoSort:boolean=false,isFront:boolean=false,isFlag:boolean=false){
        if (!list || list.length < 1) return ;

        const old:PokerData[] = this._datas.slice(0);
        for (let it of list){
            if (old.indexOf(it) === -1){
                let node:cc.Node = GameModule.insertPoker(this.node,this.pokerPrefab);
                node.setContentSize(node.getChildByName('imgFront').getContentSize());
                node.removeComponent(PokerItem);
                const item:PokerItem = node.addComponent(PokerItem);
                item.data = it;
                item.showFront = isFront;
                item.showFlag = isFlag;
            }
        }
        this.getComponent(cc.Layout).updateLayout();
        this._datas = GameLogic.sort(App.Util.concatArray(this._datas,list));
        if (!isAutoSort) return ;

        // 排序调整节点zIndex
        const itemWidth = this.node.children[0].width;
        const layout = this.getComponent(cc.Layout);
        cc.tween(layout)
        .to(0.5, { spacingX: -1*itemWidth },{easing:'quadIn'})
        .call(()=>{ 
            let index:number = 0;
            for (let it of this._datas){
                for (let node of this.node.children){
                    const item = node.getComponent(PokerItem);
                    if (item.data === it){
                        item.showFront = true;
                        item.selectHight = this.selectHight;
                        node.zIndex = index;
                        index++;
                        break ;
                    }
                }
            }
            // 调整完毕，重新排序所有节点层级关系
            this.node.childrenCount && this.node.sortAllChildren();
        }).delay(0.5)
        .to(0.5, { spacingX: this._spacing.x },{easing:'quadIn'}).start();
    }

    private _onTouchStart(event: cc.Event.EventTouch) {
        if (this._isTouch && !this._touchEvent){
            this._touchEvent = event.touch;
            this._isMove = false;
        }
    };

    private _onTouchEnd(event: cc.Event.EventTouch) {
        if (this._isTouch && this._isSameTouch(event.touch)){
            if (!this._isMove){
                const node:cc.Node = this._touchNode(event.touch.getLocation());
                const card:PokerItem = (node && node.getComponent(PokerItem));
                if (card){
                    card.select = !card.isSelect;
                    card.mask = false;
                }
            } else{
                let first:PokerItem = null;
                for (let it of this.node.children){
                    const card = it.getComponent(PokerItem);
                    if (card.isMask){
                        if (!first){
                            first = card;
                            first.select = !first.isSelect;
                        }
                        card.select = first.isSelect;
                    }
                    card.mask = false;
                }
            }
        }
        this._touchEvent = null;
        this._isMove = false;
    }

    private _onTouchMove(event: cc.Event.EventTouch): void {
        if (!this._isTouch || !this._isSameTouch(event.touch)) return ;
       
        let w = event.touch.getStartLocation().x - event.touch.getLocation().x;
        let x = w > 0 ? event.touch.getLocation().x : event.touch.getStartLocation().x;
        this._isMove = Math.abs(w) >= this._thouchAreaWidth;
        if (this._isMove){
            let y = -1;
            let rect: cc.Rect = new cc.Rect(x, y, Math.abs(w), this.node.height);
            const children = this.node.children;
            const count = children.length - 1;
            for (let i = count; i >= 0; --i) {
                const it:PokerItem = children[i].getComponent(PokerItem);
                let rt = it.node.getBoundingBoxToWorld();
                i !== count && (rt.width = this._thouchAreaWidth);
                it.mask = rect.intersects(rt);
            }
        }
    }

    /**
     * touch cancel
     * @param event 
     */
    private _onTouchCancel(event: cc.Event.EventTouch): void {
        this._onTouchEnd(event);
    }

    private _touchNode(pos:cc.Vec2):cc.Node{
        let idx = 0;
        const nodes = this.node.children;
        for (let it of nodes){
            let rc = it.getBoundingBoxToWorld();
            if (rc.xMin > pos.x) return null;
            rc.width = (idx +1 < nodes.length ? this._thouchAreaWidth : it.width);
            if (rc.contains(pos)) {
                return it;
            }
            idx++;
        }

        return null;
    }

    /**
     * 是否为相同 touch
     * @param touch 
     */
    private _isSameTouch(touch: cc.Touch): boolean {
        if (this._touchEvent) {
            return this._touchEvent.getID() === touch.getID();
        }
        return false;
    }
}
